import coursesolutions.*;
import hw4.*;
import hw3.*;
import java.io.PrintStream;

/**
  */

public class CodeGenVisitor extends NodeVisitor {
	
   /**
    * Change this method to return the visitor you want for code generation.
    * As given to you, it runs the course-sponsored solution (CourseProjectCodeGenVisitor);
    * Change the return to "new CodeGenVisitor()" to return an instance of this class instead.
    * @return the visitor for performing code generation
    */

   public static ReflectiveVisitor getCodeGenVisitor() { 
      //return new CourseCodeGenVisitor();   // Change this to CodeGenVisitor() to test your code
      return new CodeGenVisitor();
   }

   public static ReflectiveVisitor getTypeSetVisitor() { 
      return new CourseTypeSetVisitor();
   }

   private void emit(String s) {
       PrintStream ps = System.out;
       out(ps, s);
   }
   private void emit(NodeDumpable a, String s) {
      emit("; " + a.dump());
      emit(s);
   }
   /**
    *	Added plain emit NodeDumpable to allow for emitting the info before calling children 
    */
   private void emit(NodeDumpable a) {
	      emit("; " + a.dump());
   }
   private void emitComment(String s) {
      emit("; " + s);
   }
   private void skip(int num) {
     for (int i=0; i < num; ++i) emit("");
   }
   

   /** This outputs a standard prelude, with the class extending Object,
     * a dummy method for main(String[] args) that calls
     * main431
     * Thus, your test file must have a static main431 to kick things
     * off
     */
   public void visit(ClassDeclaring c) {
      emitComment("CSE431 automatically generated code file");
      emitComment("");
      emit(c, ".class public TestClasses/" + c.getName());
      emit(".super java/lang/Object");
      emit(
          "; standard initializer\n\n"+
          ".method public <init>()V\n"+
          "   aload_0\n"+
          "   invokenonvirtual java/lang/Object/<init>()V\n"+
          "   return\n"+
          ".end method\n\n"
        );
      emitComment("dummy main to call our main because we don't handle arrays");
      skip(2);
      emit(
         ".method public static main([Ljava/lang/String;)V\n"+
         "   .limit locals 1\n"+
         "   .limit stack  3\n"+
         "   invokestatic TestClasses/"+c.getName()+"/main431()V\n"+
         "   return\n"+
         ".end method\n\n"
         );
      visitChildren((AbstractNode)c);
   }
   
   /**
    * Inspects an AssignIsh node. Outputs commands to store to a register and 
    * prints information about what it is storing in the register. Also calls 
    * visitChildren on its children.
    * 
    * @param a The node to be parsed as an assignment
    */
   public void visit(AssignIsh a) {
	   emitComment("Found AssignIsh");
	   visitChildren((AbstractNode)a);
	   emit("istore_1");
	   
	   //Printing functions
	   emit("getstatic java/lang/System/out Ljava/io/PrintStream;\n"
		   +"              ldc \"Register 1 is now \"\n"
		   +"              invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V\n"
		   +"              getstatic java/lang/System/out Ljava/io/PrintStream;\n"
		   +"              iload 1\n"
		   +"              invokestatic java/lang/String/valueOf(I)Ljava/lang/String;\n"
		   +"              invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V");

   }
   
   /**
    * Inspects a ConstantInt node. Outputs a command to push the value onto the 
    * stack. Also calls VisitChildren on its children.
    * 
    * @param ci The node to be parsed as an integer
    */
   public void visit(ConstantInt ci){
	   emitComment("Found ConstantInt");
	   emit("ldc "+ci.getVal());
	   visitChildren((AbstractNode)ci);

   }

   /**
    * Inspects a ComputeIsh. Outputs a command to perform the computation from 
    * values on the stack. Also calls VisitChildren on its children.
    * 
    * @param c The node to be parsed as a computation
    */
   public void visit(ComputeIsh c){
	   emit(c);
	   visitChildren((AbstractNode)c);
	   emit("i"+c.getOperation());
	   
   }
   
   /**
    * Inspects a MethodDeclaring. Outputs a command to declare a new method 
    * including declaration and return. Also calls VisitChildren on its children.
    *  
    * @param m The node to be parsed as a method
    */
   public void visit(MethodDeclaring m){
	   String mods = "";
	   if(m.getMods().isPublic())
		   mods += "public ";
	   if(m.getMods().isStatic())
		   mods += "static ";
	   emit(m, ".method "+mods+m.getName()+"()"+m.getType().getTypeString()+"\n"
			   +"          .limit locals 10\n"
			   +"          .limit stack  30");

	   visitChildren((AbstractNode)m);
	   
	   emit("return\n"
		   +"          .end method\n");
   }

   /**
    * The default visitor that is called when there are no other visits declared 
    * for the node type.
    */
   public void defaultVisit(Object o) {
      AbstractNode n = (AbstractNode) o;
      out("Ignoring " + n.dump());
      visitChildren(n);
   }

}
